﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using KSASupport;
using KSASupport.Imaging;
using KSATouch;

namespace KSACalibrate
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer timer;
        private CalibrationWindow calibrationWindow;
        private KSATouch.KSATouch ksaTouch;
        private DepthManager activeDepthManager;

        public MainWindow()
        {
            InitializeComponent();
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(33);
            timer.Tick += Tick;
            timer.Start();
        }
        
        void Tick(object sender, EventArgs e)
        {
            RefreshCurrentDebugWindow();
        }

        private void RefreshCurrentDebugWindow()
        {
            PerformanceMetrics.Content = Performance.PerformanceManager.GetAllPerformanceData();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                ksaTouch = new KSATouch.KSATouch(KSAEngine.GetNumberOfKinects());
                PopulateKinectList();
                activeDepthManager = ksaTouch.DepthManagers[0];
                activeDepthManager.Enabled = false;
                if (activeDepthManager.IsCalibrated)
                {                   
                    //DrawRectangleFromRect(activeDepthManager.DepthManagerSettings.ScreenBoundaryRect, BoundaryCanvas);
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show("Failed to initialize a Kinect!  Please ensure it's attatched and ready. Error was: " + exc.Message);
            }
        }


        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            timer.Stop();
            Application.Current.Shutdown();
        }


        /// <summary>
        /// When a button on the left is clicked, this will figure out which 'kinect' to get data from
        /// It will also reset sliders/calibration values accordingly
        /// </summary>
        /// <param name="buttonSender"></param>
        /// <returns></returns>
        private int GetKinectIndexFromStackPanel(object buttonSender)
        {
            try
            {
                StackPanel stack = ((Button)buttonSender).Parent as StackPanel;
                int i = int.Parse(stack.Tag.ToString());                
                SetActiveDepthManager(i);
                DrawRectangleFromRect(activeDepthManager.DepthManagerSettings.ScreenBoundaryRect,BoundaryCanvas);
                return i;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private void SetActiveDepthManager(int i)
        {
            activeDepthManager = null;
            HighDistanceAboveSlider.Value = ksaTouch.DepthManagers[i].DepthManagerSettings.HighestDepthFromSurface;
            LowDistanceAboveSlider.Value = ksaTouch.DepthManagers[i].DepthManagerSettings.LowestDepthFromSurface;
            MinBlobSizeSlider.Value = ksaTouch.DepthManagers[i].DepthManagerSettings.MinimumBlobPixelCount;
            activeDepthManager = ksaTouch.DepthManagers[i];
            UpdateSliders();
        }


        private void InitializeDebugWindow(DepthManager manager)
        {
            DebugGrid.Children.Clear();
            calibrationWindow = null;
            calibrationWindow = new CalibrationWindow(manager);
            DebugGrid.Children.Add(calibrationWindow);

            // Set thresholds based on slider values
            //KSATouch.DepthManager.DepthManagerSettings.MinDepthSetting = (int)thresholdMin.Value;
            //KSATouch.DepthManager.DepthManagerSettings.MaxDepthSetting = (int)thresholdMax.Value;            
            
            
            calibrationWindow.StartRendering();
        }


        // Hardcoded to handle first Kinect. In the future we'll make this 'dynamic'
        // for multiple sensors.
        private void Debug_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);

            InitializeDebugWindow(ksaTouch.DepthManagers[KinectID]);
            calibrationWindow.SetMode(DebugWindowMode.Raw);
        }


        private void CalibrationButton_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            DepthManager manager = ksaTouch.DepthManagers[KinectID];
            manager.IsCalibrated = false;
            InitializeDebugWindow(manager);
            calibrationWindow.SetMode(DebugWindowMode.Calibrating);
            manager.Calibrate();           
        }

        private void ThresholdButton_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            InitializeDebugWindow(ksaTouch.DepthManagers[KinectID]);
            calibrationWindow.SetMode(DebugWindowMode.Threshold);
        }

        private void highThresholdButton_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            InitializeDebugWindow(ksaTouch.DepthManagers[KinectID]);
            calibrationWindow.SetMode(DebugWindowMode.HoverThreshold);
        }

        void RawImageButton_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            InitializeDebugWindow(ksaTouch.DepthManagers[KinectID]);
            calibrationWindow.SetMode(DebugWindowMode.CalibratedImage);
        }

        private void analyzeCalibration_Click(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            InitializeDebugWindow(ksaTouch.DepthManagers[KinectID]);
            calibrationWindow.SetMode(DebugWindowMode.Analysis);
        }


        private void DebugFormButton_Click(object sender, RoutedEventArgs e)
        {            
            int KinectID = GetKinectIndexFromStackPanel(sender);
            DebugForm DebugFormWindow = new DebugForm(ksaTouch, KinectID);
            DebugFormWindow.Show();
        }

        private void TouchCalibration_Click(object sender, RoutedEventArgs e)
        {            
            //KSATouch.KSATouch ksaTouch = new KSATouch.KSATouch();
            int KinectID = GetKinectIndexFromStackPanel(sender);
            TouchCalibration touchCalibrationWindow = new TouchCalibration(ksaTouch,KinectID);
            touchCalibrationWindow.Show();
        }

        /// <summary>
        /// This canvas tracks when a 'rectangle' is drawn on it, and sets the calibration such that it restricts the sensitive area to just the screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BoundaryCanvas_MouseDown_1(object sender, MouseButtonEventArgs e)
        {
            Point start = new Point(e.GetPosition(BoundaryCanvas).X, e.GetPosition(BoundaryCanvas).Y);
            Rect rect = new Rect(start, new Point(start.X, start.Y));
            DrawRectangleFromRect(rect, BoundaryCanvas);


            BoundaryCanvas.MouseMove +=BoundaryCanvas_MouseMove;
            BoundaryCanvas.MouseUp += BoundaryCanvas_MouseUp;
        }

        void BoundaryCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Point start = new Point(e.GetPosition(BoundaryCanvas).X, e.GetPosition(BoundaryCanvas).Y);
            Rect rect = new Rect(start, new Point(start.X, start.Y));
            DrawRectangleFromRect(rect, BoundaryCanvas);

            this.MouseMove -= new MouseEventHandler(BoundaryCanvas_MouseMove);
            BoundaryCanvas.MouseUp -= new MouseButtonEventHandler(BoundaryCanvas_MouseUp);
        }

        void BoundaryCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.MouseDevice.LeftButton == MouseButtonState.Pressed)
            {
                Rectangle rectangle = new Rectangle();
                for (int i = 0; i < BoundaryCanvas.Children.Count; i++)
                {
                    if ((BoundaryCanvas.Children[i].GetType() == typeof(Rectangle)))
                    {
                        rectangle = (Rectangle)(BoundaryCanvas.Children[i]);
                        break;
                    }
                }
                Rect rect = (Rect)rectangle.Tag;
                Point end = new Point(e.GetPosition(BoundaryCanvas).X, e.GetPosition(BoundaryCanvas).Y);
                Rect rectNew = new Rect(rect.TopLeft, end);
                DrawRectangleFromRect(rectNew, BoundaryCanvas);
            }
        }
        private void DrawRectangleFromRect(Rect rect, Canvas element)
        {
            for (int i = 0; i < element.Children.Count; i++)
            {
                if (element.Children[i].GetType() == typeof(Rectangle))
                {
                    element.Children.Remove(element.Children[i]);
                }
            }
            Rectangle rectangle = new Rectangle();
            rectangle.Width = rect.Width;
            rectangle.Height = rect.Height;
            rectangle.Tag = rect;
            rectangle.Fill = null;
            rectangle.Stroke = Brushes.Green;
            rectangle.StrokeThickness = 3;

            Canvas.SetLeft(rectangle, rect.X);
            Canvas.SetTop(rectangle, rect.Y);

            activeDepthManager.DepthManagerSettings.ScreenBoundaryRect = rect;
            activeDepthManager.DepthManagerSettings.SaveSettings();
            element.Children.Add(rectangle);
        }

        private void PopulateKinectList()
        {
            for (int i = 0; i < ksaTouch.DepthManagers.Count(); i++)
            {
                StackPanel panel = new StackPanel();
                panel.Tag = i;

                System.Windows.Controls.Label label = new System.Windows.Controls.Label();
                label.Content = "Kinect: " + i.ToString();
                label.Background = Brushes.Blue;
                label.Foreground = Brushes.White;
                panel.Children.Add(label);

                Button RawImageButton = new Button();
                RawImageButton.Content = "Show Raw Image";
                RawImageButton.Click += RawImageButton_Click;
                panel.Children.Add(RawImageButton);

                Button debugButton = new Button();
                debugButton.Click += Debug_Click;
                debugButton.Content = "Show Debug";
                panel.Children.Add(debugButton);

                Button calibrationButton = new Button();
                calibrationButton.Content = "Calibrate";
                calibrationButton.Click += CalibrationButton_Click;
                panel.Children.Add(calibrationButton);

                Button thresholdButton = new Button();
                thresholdButton.Content = "Show Threshold";
                thresholdButton.Click +=ThresholdButton_Click;
                panel.Children.Add(thresholdButton);

                Button highThresholdButton = new Button();
                highThresholdButton.Content = "Show Hover";
                highThresholdButton.Click += highThresholdButton_Click;
                panel.Children.Add(highThresholdButton);

                Button TouchCalibration = new Button();
                TouchCalibration.Content = "Touch Calibration";
                TouchCalibration.Click +=TouchCalibration_Click;
                panel.Children.Add(TouchCalibration);

                Button DebugFormButton = new Button();
                DebugFormButton.Content = "Debug Touch";
                DebugFormButton.Click += DebugFormButton_Click;
                panel.Children.Add(DebugFormButton);

                Button AnalyzeButton = new Button();
                AnalyzeButton.Content = "Analyze Calibration";
                AnalyzeButton.Click += analyzeCalibration_Click;
                panel.Children.Add(AnalyzeButton);

                CheckBox DepthManagerEnabled = new CheckBox();
                DepthManagerEnabled.Content = "Enable";
                DepthManagerEnabled.Checked += DepthManagerEnabled_Checked;
                DepthManagerEnabled.Unchecked += DepthManagerEnabled_Checked;
                panel.Children.Add(DepthManagerEnabled);


                KinectList.Children.Add(panel);
            }           
        }



        void DepthManagerEnabled_Checked(object sender, RoutedEventArgs e)
        {
            int KinectID = GetKinectIndexFromStackPanel(sender);
            activeDepthManager.Enabled = ((CheckBox)sender).IsChecked.Value;
        }

        private void SaveDepthImage(object sender, RoutedEventArgs e)
        {
            KinectImageConversion.SaveIntArrayToFile(ksaTouch.DepthManager.ThresholdImage, "Capture" + DateTime.Now.ToString() + ".txt");
            
        }

        private void sliderValue_Changed(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (activeDepthManager == null)
                return;

            // Hacky way of ignoring this on first 'click'. Otherwise this will override all of our settings each time it is initialized.
            if ((MinBlobSizeSlider.Value == 0) || (HighDistanceAboveSlider.Value == 0) || (LowDistanceAboveSlider.Value==0))
                return;

            activeDepthManager.DepthManagerSettings.MinimumBlobPixelCount = (int)MinBlobSizeSlider.Value;
            activeDepthManager.DepthManagerSettings.HighestDepthFromSurface = (int)HighDistanceAboveSlider.Value;
            activeDepthManager.DepthManagerSettings.LowestDepthFromSurface = (int)LowDistanceAboveSlider.Value;

            UpdateSliders();
            
        }

        private void UpdateSliders()
        {
            MinBlobSizeValue.Content = MinBlobSizeSlider.Value.ToString();
            HighDistanceValue.Content = HighDistanceAboveSlider.Value.ToString();
            LowDistanceValue.Content = LowDistanceAboveSlider.Value.ToString();
        }

        private void Save_Click(object sender, RoutedEventArgs e)
        {
            activeDepthManager.DepthManagerSettings.SaveSettings();
        }
    }
}
